// see LICENSE.incore for more details on licensing terms
/*
Author: Neel Gala, neelgala@incoresemi.com
Created on: Tuesday 28 April 2020 10:06:56 PM IST

*/
package DebugSoc ;

import FIFOF        :: * ;
import Vector       :: * ;
import SpecialFIFOs :: * ;
import FIFOF        :: * ;
import GetPut       :: * ;
import Connectable  :: * ;
import Clocks       :: * ;

import Soc          :: * ;
import qspi         :: * ;
import uart         :: * ;
import gpio         :: * ;
import pwm          :: * ;
import spi          :: * ;
import axi4         :: * ;
import ccore_types  :: * ;
`ifdef bscan2e
  import xilinxdtm::*;
`else
  import jtagdtm      :: * ;
`endif
import riscv_debug  :: * ;
import riscv_debug_types :: * ;

`include "Logger.bsv"
`include "Soc_map.bsv"

interface Ifc_DebugSoc;
  (*prefix="uart0"*)
  interface RS232#(16) ifc_uart0;
  (*always_ready, always_enabled, prefix=""*)
  method Action boot_config ((*port = "boot_config"*) Bit#(2) conf);
  (*prefix=""*)
  interface GPIO#(22)  ifc_gpio;
  (*prefix=""*)
  interface PWMIO#(`channels, `comp_out_en)  ifc_pwm;
  (*prefix="spi0"*)
  interface SPI_IO#(1) ifc_spi0;
  (*prefix="spi1"*)
  interface SPI_IO#(2) ifc_spi1;
  (*prefix="spi2"*)
  interface SPI_IO#(1) ifc_spi2;
  (*prefix="ddr"*)
  interface Ifc_axi4_master#(`AXI4ID, `paddr, 64, `User) ddr_slave;
  (*prefix="open"*)
  interface Ifc_axi4_master#(`AXI4ID, `paddr, 64, `User) open_slave;
  // ------------- JTAG IOs ----------------------//
  (*always_enabled,always_ready*)
  method Action wire_tms(Bit#(1)tms_in);
  (*always_enabled,always_ready*)
  method Action wire_tdi(Bit#(1)tdi_in);
  (*always_enabled,always_ready*)
  method Bit#(1)wire_tdo;
`ifdef bscan2e //---  Shift Register Control ---//
  (*always_enabled,always_ready*)
  method Action wire_capture(Bit#(1) capture_in);
  (*always_enabled,always_ready*)
  method Action wire_run_test(Bit#(1) run_test_in);
  (* always_enabled,always_ready*)
  method Action wire_sel (Bit#(1) sel_in);
  (* always_enabled,always_ready*)
  method Action wire_shift (Bit#(1) shift_in);
  (* always_enabled,always_ready*)
  method Action wire_update (Bit#(1) update_in);
`endif
`ifdef rtldump
  interface Get#(DumpType) io_dump;
`endif
  // ---------------------------------------------//
  interface Reset soc_reset;
  (*always_ready, always_enabled, prefix="qspi0"*)
  interface QSPI_out qspi_io;
endinterface:Ifc_DebugSoc

(*synthesize*)
module mkDebugSoc#(Clock tck_clk, Reset trst)(Ifc_DebugSoc);

  let curr_clk<-exposeCurrentClock;
  let curr_reset<-exposeCurrentReset;

  MakeResetIfc ndm_reset <-mkReset(0,False,curr_clk);            // create a new reset for curr_clk
  Reset derived_reset <- mkResetEither(ndm_reset.new_rst,curr_reset);     // OR default and new_rst

  Ifc_Soc soc <- mkSoc(reset_by derived_reset);
  // null crossing registers to transfer input signals from current_domain to tck domain
  CrossingReg#(Bit#(1)) tdi<-mkNullCrossingReg(tck_clk,0);
  CrossingReg#(Bit#(1)) tms<-mkNullCrossingReg(tck_clk,0);
  // null crossing registers to transfer signals from tck to curr_clock domain.
  CrossingReg#(Bit#(1)) tdo<-mkNullCrossingReg(curr_clk,0,clocked_by tck_clk, reset_by trst);
`ifdef bscan2e
  CrossingReg#(Bit#(1)) capture <- mkNullCrossingReg(tck_clk,0);
	CrossingReg#(Bit#(1)) run_test <- mkNullCrossingReg(tck_clk,0);
	CrossingReg#(Bit#(1)) sel <- mkNullCrossingReg(tck_clk,0);
	CrossingReg#(Bit#(1)) shift <- mkNullCrossingReg(tck_clk,0);
	CrossingReg#(Bit#(1)) update <- mkNullCrossingReg(tck_clk,0);
`endif

  // Tap Controller jtag_tap
`ifdef bscan2e
  Ifc_xilinxdtm jtag_tap <- mkxilinxdtm(clocked_by tck_clk, reset_by trst);
`else
  Ifc_jtagdtm jtag_tap <- mkjtagdtm(clocked_by tck_clk, reset_by trst);
`endif
  Ifc_riscv_debug debug_module <- mkriscv_debug;

  // synFIFOs to transact data between JTAG and debug module
  SyncFIFOIfc#(Bit#(41)) sync_request_to_dm <-mkSyncFIFOToCC(1,tck_clk,trst);
  SyncFIFOIfc#(Bit#(34)) sync_response_from_dm <-mkSyncFIFOFromCC(1,tck_clk);

  mkConnection (soc.debug_server ,debug_module.hart);
  mkConnection (debug_module.debug_master, soc.debug_master);

  rule rl_generate_ndmreset(debug_module.getNDMReset == 1);
    ndm_reset.assertReset;
    `logLevel( debugsoc, 0, $format("DebubSoc: Asserting NDM Reset"))
  endrule
           // ----------- Connect JTAG IOs through null-crossing registers ------ //
  rule assign_jtag_inputs;
    jtag_tap.tms_i(tms.crossed);
    jtag_tap.tdi_i(tdi.crossed);
  `ifdef bscan2e
    jtag_tap.capture_i(capture.crossed);
  	jtag_tap.run_test_i(run_test.crossed);
  	jtag_tap.sel_i(sel.crossed);
  	jtag_tap.shift_i(shift.crossed);
  	jtag_tap.update_i(update.crossed);
  `else
    jtag_tap.bs_chain_i(0);
    jtag_tap.debug_tdi_i(0);
  `endif
  endrule

  rule assign_jtag_output;
    tdo <= jtag_tap.tdo(); //  Launched by a register clocked by inverted tck
  endrule
          // ------------------------------------------------------------------- //

  // capture jtag tap request into a syncfifo first.
  rule connect_tap_request_to_syncfifo;
    let x<-jtag_tap.request_to_dm;
    sync_request_to_dm.enq(zeroExtend(x));

  // send captured synced jtag tap request to the debug module
  endrule
  rule read_synced_request_to_dm;
    sync_request_to_dm.deq;
    debug_module.dtm.putCommand.put(sync_request_to_dm.first);
  endrule

  // collect debug response into a syncfifo
  rule connect_debug_response_to_syncfifo;
    let x <- debug_module.dtm.getResponse.get;
    sync_response_from_dm.enq(x);
  endrule

  // send synced debug response back to the JTAG
  rule read_synced_response_from_dm;
    sync_response_from_dm.deq;
    jtag_tap.response_from_dm(sync_response_from_dm.first);
  endrule
  method Action wire_tms(Bit#(1)tms_in);
    tms <= tms_in;
  endmethod
  method Action wire_tdi(Bit#(1)tdi_in);
    tdi <= tdi_in;
  endmethod
  method Bit#(1)wire_tdo;
    return tdo.crossed();
  endmethod

`ifdef bscan2e
  method Action wire_capture(Bit#(1) capture_in);
    capture <= capture_in;
  endmethod
  method Action wire_run_test(Bit#(1) run_test_in);
    run_test <= run_test_in;
  endmethod
  method Action wire_sel (Bit#(1) sel_in);
    sel <= sel_in;
  endmethod
  method Action wire_shift (Bit#(1) shift_in);
    shift <= shift_in;
  endmethod
  method Action wire_update (Bit#(1) update_in);
    update <= update_in;
  endmethod
`endif

  interface ifc_uart0 = soc.ifc_uart0;
  method boot_config   = soc.boot_config;
  interface ifc_gpio   = soc.ifc_gpio;
  interface ifc_pwm    = soc.ifc_pwm;
  interface ifc_spi0   = soc.ifc_spi0;
  interface ifc_spi1   = soc.ifc_spi1;
  interface ifc_spi2   = soc.ifc_spi2;
  interface ddr_slave  = soc.ddr_slave ;
  interface open_slave = soc.open_slave;

  interface soc_reset = derived_reset;
`ifdef rtldump
  interface io_dump = soc.io_dump;
`endif
  interface qspi_io = soc.qspi_io;
                                                                                                  
endmodule:mkDebugSoc
endpackage: DebugSoc

